package com.fyh.hiring.common.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;
import java.util.List;

import com.fyh.hiring.common.DBObject;

/**
 * <pre>
 *  JDBC的工具类
 *   封装工具类其实就用到了两点：
 *     1.让工具类内部操作，如果我要使用里面的东西，那么你就给我返回回来——>返回值
 *     2.我只需里面的操作，你就把操作给我做好了，然后我传个参数就可以了哈——>传参数
 *     
 * 当我们在操作时，如果一个方法里传入的参数太多，那么我们就把这些参数封装成对象，通过里面的get方法，把
 * 变量(参数)取出来
 *  @author Administrator
 * </pre>
 */
public class JDBCUtil {

	public static final String DRIVER = "com.mysql.jdbc.Driver";
	public static final String URL = "jdbc:mysql://localhost:3306/hiring"; // test是数据库
	public static final String USER = "root";
	public static final String PASSWOR = "root";

	/**
	 * 构造方法私有化
	 */
	private JDBCUtil() {
	}

	/**
	 * JDBC的加载驱动以及连接数据库，重复使用所以做成方法，返回一个connection对象
	 */
	public static Connection getconnection() {

		// 1.加载驱动
		try {
			Class.forName(DRIVER);
			// 2.获取连接
			Connection connection = DriverManager.getConnection(URL, USER,
					PASSWOR);
			return connection;
		} catch (ClassNotFoundException | SQLException e) {
			e.printStackTrace();
			// catch里也要写返回值!!throw可以当返回值用？
			throw new RuntimeException("获取连接失败" + e.getMessage()); // 这里转义，避免了调用者去处理

		}

	}

	/****************************** 针对关闭的方法 *********************************/
	/**
	 * 针对连接connection的单独关闭
	 */
	public static void close(Connection connection) {

		if (connection != null) {
			try {
				connection.close();
			} catch (SQLException e) {
				e.printStackTrace();
				throw new RuntimeException("关闭数据库连接失败....");
			}
		}
	}

	/**
	 * 针对statement对象的单独关闭，单独关闭的时候可以直接掉自己close
	 */
	public static void close(Statement statement) {
		try {
			if (statement != null) {
				statement.close();
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new RuntimeException("关闭statement对象失败...");
		}
	}

	/**
	 * 针对查询的resultset对象的单独关闭
	 */
	public static void close(ResultSet resultset) {
		try {
			if (resultset != null) {
				resultset.close();

			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new RuntimeException("关闭resultset对象失败....");
		}
	}

	/**
	 * 针对preparedstatement对象的单独关闭
	 */
	public static void close(PreparedStatement preparedStatement) {
		try {
			if (preparedStatement != null) {
				preparedStatement.close();
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new RuntimeException("关闭preparedStatement对象失败....");
		}
	}

	/**
	 * 针对statement对象以及connection的关闭(statement基本上已经被preparedstatement取代)
	 */
	public static void close(Statement statement, Connection connection) {
		// 直接调用前面写好的关闭方法
		close(statement);
		close(connection);
	}

	/**
	 * 针对preparedstatement对象以及connecton的关闭
	 */
	public static void close(PreparedStatement preparedStatement,
			Connection connection) {
		// 遵循先打开的后关闭的原则
		/**
		 * 由于我前面已经写好了单独关闭的方法，所以直接使用即可
		 */
		close(preparedStatement);
		close(connection);

	}

	/**
	 * 三个对象的关闭
	 */
	public static void close(ResultSet resultset, Statement statement,
			Connection connection) {
		// 直接调用前面的关闭方法
		close(resultset);
		close(statement);
		close(connection);

	}

	/**
	 * 三个对象的关闭
	 */
	public static void close(ResultSet resultset,
			PreparedStatement preparedStatement, Connection connection) {
		// 直接调用前面的关闭方法
		close(resultset);
		close(preparedStatement);
		close(connection);

	}

	/**
	 * 关闭DBObject对象(这个类里面全是需要关闭的资源)
	 */
	public static void close(DBObject dbObject) {

		try {
			if (dbObject != null) {

				dbObject.getResultSet().close();
				dbObject.getPreparedStatement().close();
				dbObject.getConnection().close();
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	/***************************** 执行DML操做 **********************************/

	/**
	 * 我只关注受影响的行数,所以返回值是int；由于参数不确定，所以我可以使用集合
	 * 
	 * @throws SQLException
	 */
	public static int executeUpdate(String sql, List<Object> paramList)
			throws SQLException {
		Connection connection = null;
		PreparedStatement preparedStatement = null;

		try {
			connection = JDBCUtil.getconnection();

			/**
			 * 一个?对应一个参数，如何把参数动态的赋给每个?,注意第一个?就是1，无论出现在哪里
			 */
			/**************************** 6的操做 ***********************************/
			preparedStatement = connection.prepareStatement(sql);
			setparamter(paramList, preparedStatement);

			/**
			 * preparedstatement.executeUpdate(sql)这里不能用参数的，不然运行会出错！！！！
			 */
			int rows = preparedStatement.executeUpdate();
			return rows;

		} catch (SQLException e) {

			throw e;// 这样只是起了一个记录哪里出错的作用，还是会跑出去！！！！！！！！！！！！！！！！！

		} finally {
			JDBCUtil.close(preparedStatement, connection);
		}
	}

	/**************************************************/

	/**
	 * <pre>
	 * 我们这里用了List集合，如果只有一个参数，我同样还要去new，这样很麻烦，有没有更好的办法针对参数少的？
	 *   这里用到了可变参数,以及两者之间的转换
	 * @param sql
	 * @return
	 * </pre>
	 * 
	 * @throws SQLException
	 */
	public static int executeUpdate(String sql, Object... params)
			throws SQLException {
		// 里面的内容都是一样的，直接调用
		return executeUpdate(sql, Arrays.asList(params)); // 为什么要转成集合？因为这个方法本身就是集合啊
		/** 因为这里调用的是上面的excuteUpdate方法，所以要转成list集合啊 */
	}

	/*************************** DQL的操做 ************************************/

	/**
	 * 
	 * @param sql
	 * @param paramList
	 * @return
	 * @throws SQLException
	 */
	public static DBObject executeQuery(String sql, List<Object> paramList)
			throws SQLException {
		Connection connection = JDBCUtil.getconnection();

		/**
		 * 一个?对应一个参数，如何把参数动态的赋给每个?,注意第一个?就是1，无论出现在哪里
		 */
		/**************************** 6的操做 ***********************************/
		PreparedStatement preparedStatement = connection.prepareStatement(sql);
		setparamter(paramList, preparedStatement);

		/**
		 * preparedstatement.executeUpdate(sql)这里不能用参数的，不然运行会出错！！！！
		 */
		ResultSet resultSet = preparedStatement.executeQuery();

		/**
		 * 这里出现了一个问题，这里如果关了，那么返回出去的结果是无法执行的，所以要想办法把这两个对象也返回出去<br/>
		 * 但是具体怎么做呢，还是同样的，封装成对象就可以哈
		 */
		// JDBCUtil.close(preparedStatement, connection);

		return new DBObject(connection, preparedStatement, resultSet);

	}

	/*********************************************************************/

	/**
	 * <pre>
	 * executeUpdate(String sql, List<Object> paramList)和executeQuery(String
	 * sql, List<Object> paramList)这两个方法
	 * 里面还有重复的，就是if和for，那么秉着有重复就提取的原则，所以还是要把他们单独拿出来！！！！！！！
	 *  具体做法：
	 *    首先要选中有相同的if那一大块，然后alt+shift+T，弹出的菜单栏中点击extract method(提取方法)
	 *    或者，选中相同的那一块，然后点击菜单栏里的refactor，然后再弹出里选择extract method(提取方法)
	 * </pre>
	 */
	private static void setparamter(List<Object> paramList,
			PreparedStatement preparedStatement) throws SQLException {
		if (paramList != null) { // delete from fyh ;这样参数就是null

			for (int i = 0; i < paramList.size(); i++) {
				/************** 这里同样是设置参数 *****************/
				/** 一个?对应一个参数，但是?是从1开始计算的！！第一个?无论出现在哪里，都是1！！！ ***/
				preparedStatement.setObject(i + 1, paramList.get(i));
				/**
				 * <pre>
				 * preparedStatement.setObject(i + 1, paramList.get(i));当i=0时，就是
				 * 第一个?对应get(0)也就是第一个参数
				 * </pre>
				 */

			}
		}
	}

	/***********************************************************************/

	/**
	 * <pre>
	 * 我们这里用了List集合，如果只有一个参数，我同样还要去new，这样很麻烦，有没有更好的办法针对参数少的？
	 *   这里用到了可变参数,以及两者之间的转换
	 * @param sql
	 * @return
	 * </pre>
	 * 
	 * @throws SQLException
	 */
	public static DBObject executeQuery(String sql, Object... params)
			throws SQLException {
		// 里面的内容都是一样的，直接调用
		return executeQuery(sql, Arrays.asList(params)); // 为什么要转成集合？
	}

}
